


import tempfile
from sqlalchemy import select,delete,update,insert
from .db import packageList
from sqlalchemy.orm import scoped_session
class Storage:
    path:str=''
    db:scoped_session=None
    # // ProcessByPrefix iterates through all entries where key starts with prefix and calls
    # // StorageProcessor on key value pair
    def ProcessByPrefix(s,prefix, proc) :
        # iterator := s.db.NewIterator(None, None)
        # defer iterator.Release()

        # for ok := iterator.Seek(prefix); ok && bytes.HasPrefix(iterator.Key(), prefix); ok = iterator.Next() {
        # 	err := proc(iterator.Key(), iterator.Value())
        # 	if err is not None  {
        # 		return err
        # 	}
        # }

        # return None
        value= s.db.execute(select(packageList).where(packageList.key.like('{}%'.format(prefix)))).scalars().all()
        for i in value:
            proc(i.key,i.value)

        # return value

    # // CreateTemporary creates new DB of the same type in temp dir
    def CreateTemporary(s) :
        import database
        tempdir= tempfile.TemporaryDirectory("", "aptly")

        db= database.internalOpen(tempdir.name, True)
        
        storage=Storage()
        storage.db=db
        storage.path=tempdir.name
        return Storage


    # // Get key value from database
    def  Get(s,key ) :
        
        value= s.db.execute(select(packageList).where(packageList.key==key)).scalars().first()
        if value is not  None:
            return value.value
        return None

    # // Put saves key to database, if key has the same value in DB already, it is not saved
    def  Put(s,key, value ):
        old=s.db.execute(select(packageList).where(packageList.key==key)).scalars().first()
        if old is not None:
            if old.value == value:
                return None
            return s.db.execute(update(packageList).where(packageList.key==key).values(value=value))
        return s.db.execute(insert(packageList).values(key=key,value=value))


    # // Delete removes key from DB
    def  Delete(s,key ) :
        return s.db.execute(delete(packageList).where(packageList.key==key))



    # // KeysByPrefix returns all keys that start with prefix
    def KeysByPrefix(s,prefix ) :
        result =s.db.execute(select(packageList).where(packageList.key.like("%{}".format(prefix)))).scalars().all()
        return result


    # // FetchByPrefix returns all values with keys that start with prefix
    def  FetchByPrefix(s,prefix ) :
        result =s.db.execute(select(packageList).where(packageList.value.like("%{}".format(prefix)))).scalars().all()
        return result


    # // HasPrefix checks whether it can find any key with given prefix and returns true if one exists
    def  HasPrefix(s ,prefix) ->bool :
    	# iterator := s.db.NewIterator(None, None)
    	# defer iterator.Release()
    	# return iterator.Seek(prefix) && bytes.HasPrefix(iterator.Key(), prefix)
        result =s.db.execute(select(packageList).where(packageList.key.like("%{}".format(prefix)))).scalars().all()
        return bool(result)
    # }



    # // Close finishes DB work
    def  Close(s) :
        # if s.db is None:
        # 	return None
        
        # s.db.close()
        # s.db = None
        # return err
        
        return s.db.close()

    # // Reopen tries to open (re-open) the database
    def   Open(s) :
        import database
        if s.db is not None :
            return None
        s.db = database.internalOpen(s.path, False)


    # // CreateBatch creates a Batch object
    # def (s *storage) CreateBatch() database.Batch {
    # 	return &batch{
    # 		db: s.db,
    # 		b:  &leveldb.Batch{},
    # 	}
    # }

    # // OpenTransaction creates new transaction.
    # def (s *storage) OpenTransaction() (database.Transaction, error) {
    # 	t, err := s.db.OpenTransaction()
    # 	if err is not None  {
    # 		return None, err
    # 	}

    # 	return &transaction{t: t}, None
    # }

    # // CompactDB compacts database by merging layers
    # def (s *storage) CompactDB() error {
    # 	return s.db.CompactRange(util.Range{})
    # }

    # // Drop removes all the DB files (DANGEROUS!)
    # def (s *storage) Drop() error {
    # 	if s.db is not None  {
    # 		return errors.New("DB is still open")
    # 	}

    # 	return os.RemoveAll(s.path)
    # }

# // Check interface
# var (
# 	_ database.Storage = &storage{}
# )
